package Leetcode100Hot;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

/*
找到字符串中所有字母异位词
给定两个字符串 s 和 p，找到 s 中所有 p 的 异位词 的子串，返回这些子串的起始索引。不考虑答案输出的顺序。

示例 1:
输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。
示例 2:
输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。

提示:
1 <= s.length, p.length <= 3 * 104
s 和 p 仅包含小写字母
 */
public class _30找到字符串中所有字母异位词 {

    @Test
    public void test() {
        System.out.println(findAnagrams2("cbaebabacd", "abc"));
    }

    //WA 超时 64/65
    public List<Integer> findAnagrams(String s, String p) {
        HashSet<Character> set = new HashSet<>();
        for (int i = 0; i < p.length(); i++) {
            char c = p.charAt(i);
            set.add(c);
        }
        List<Integer> res = new ArrayList<>();
        for (int i = 0; i < s.length() - p.length() + 1; i++) {
            char c = s.charAt(i);
            /*for (int j = 0; j < p.length(); j++) {
                if (c == p.charAt(j)) {
                    if (findAnagram(s.substring(i,i + p.length()),p)){
                        res.add(i);
                    }
                    break;
                }
            }*/
            if (set.contains(c)) {
                boolean anagram = findAnagram(s.substring(i, i + p.length()), p);
                if (anagram) {
                    res.add(i);
                }
            }
        }
        return res;
    }

    public boolean findAnagram(String s, String p) {
        char[] sCharArray = s.toCharArray();
        Arrays.sort(sCharArray);
        char[] pCharArray = p.toCharArray();
        Arrays.sort(pCharArray);
        return Arrays.equals(sCharArray, pCharArray);
    }

    //官解：方法一：滑动窗口
    /*
    作者：力扣官方题解
    链接：https://leetcode.cn/problems/find-all-anagrams-in-a-string/solutions/1123971/zhao-dao-zi-fu-chuan-zhong-suo-you-zi-mu-xzin/
     */
    public List<Integer> findAnagrams2(String s, String p) {
        int sLen = s.length(), pLen = p.length();
        if (sLen < pLen) {
            return new ArrayList<Integer>();
        }

        List<Integer> ans = new ArrayList<Integer>();  //  arraylist是有序的，有索引的
        int[] sCount = new int[26];  // 整数，记录26个小写字母的索引，a对应索引0，b对应索引1..... 每个索引对应的值，为该字母出现的次数，初始次数都是0；
        int[] pCount = new int[26];
        for (int i = 0; i < pLen; i++) { // 按照p字符数组的长度去遍历
            ++sCount[s.charAt(i) - 'a']; // s.chartAt(i)得到此处的字母，-"a"，得到该字母的索引，++表示统计该字母的出现次数。
            ++pCount[p.charAt(i) - 'a'];
        }
        //判断放置处是否有异位词。若相等，则表明s的前几位就是p的异位词。起始索引即为0.
        if (Arrays.equals(sCount, pCount)) {
            ans.add(0);
        }

        for (int i = 0; i < sLen - pLen; ++i) {
            --sCount[s.charAt(i) - 'a']; // 减去滑动窗口的第0位，即，窗口变为1，2
            ++sCount[s.charAt(i + pLen) - 'a']; // 加上窗口的第plen位(第3位)，即，窗口变为1，2，3
            // 完成了一次滑动窗口
            // 若相等，存在异位词，起始索引为i+1
            if (Arrays.equals(sCount, pCount)) {
                ans.add(i + 1);
            }
        }

        return ans;
    }

    //官解：方法二：优化滑动窗口
    /*
    作者：力扣官方题解
    链接：https://leetcode.cn/problems/find-all-anagrams-in-a-string/solutions/1123971/zhao-dao-zi-fu-chuan-zhong-suo-you-zi-mu-xzin/
     */
    class Solution {
        public List<Integer> findAnagrams(String s, String p) {
            int sLen = s.length(), pLen = p.length();

            if (sLen < pLen) {
                return new ArrayList<Integer>();
            }

            List<Integer> ans = new ArrayList<Integer>();
            int[] count = new int[26];
            for (int i = 0; i < pLen; ++i) {
                ++count[s.charAt(i) - 'a'];
                --count[p.charAt(i) - 'a'];
            }

            int differ = 0;
            for (int j = 0; j < 26; ++j) {
                if (count[j] != 0) {
                    ++differ;
                }
            }

            if (differ == 0) {
                ans.add(0);
            }

            for (int i = 0; i < sLen - pLen; ++i) {
                if (count[s.charAt(i) - 'a'] == 1) {  // 窗口中字母 s[i] 的数量与字符串 p 中的数量从不同变得相同
                    --differ;
                } else if (count[s.charAt(i) - 'a'] == 0) {  // 窗口中字母 s[i] 的数量与字符串 p 中的数量从相同变得不同
                    ++differ;
                }
                --count[s.charAt(i) - 'a'];

                if (count[s.charAt(i + pLen) - 'a'] == -1) {  // 窗口中字母 s[i+pLen] 的数量与字符串 p 中的数量从不同变得相同
                    --differ;
                } else if (count[s.charAt(i + pLen) - 'a'] == 0) {  // 窗口中字母 s[i+pLen] 的数量与字符串 p 中的数量从相同变得不同
                    ++differ;
                }
                ++count[s.charAt(i + pLen) - 'a'];

                if (differ == 0) {
                    ans.add(i + 1);
                }
            }

            return ans;
        }
    }

}
